package com.bfly.core.base.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bfly.common.json.JsonUtil;
import com.bfly.common.page.Pager;
import com.bfly.common.reflect.ClassUtils;
import com.bfly.core.cache.EhCacheUtil;
import com.bfly.core.context.CacheableThreadLocal;
import com.bfly.core.context.PagerThreadLocal;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.ehcache.EhCacheCache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 长沙商脉科技有限公司
 * @date 2018/12/7 13:32
 */
@Service
@Transactional(rollbackFor = Exception.class, readOnly = true)
public abstract class BaseServiceImpl<T, ID extends Serializable> implements IBaseService<T, ID> {

    private Logger logger = LoggerFactory.getLogger(BaseServiceImpl.class);

    private BaseMapper<T> baseDao;

    /**
     * 获得缓存 每一个实体类都有自己的缓存域
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/6/23 21:27
     */
    public EhCacheCache getCache() {
        String entityName = ClassUtils.getSuperClassGenricType(getClass()).getName();
        EhCacheCache cache = (EhCacheCache) EhCacheUtil.getCache(entityName);
        return cache;
    }

    /**
     * 默认对查询结果缓存
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/5 20:53
     */
    private boolean cacheAble() {
        return CacheableThreadLocal.get();
    }

    /**
     * 查询参数转换为缓存的KEY
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/6/23 21:29
     */
    public String paramMapToCacheKey(Map<String, ?>... params) {
        if (params == null) {
            return "null:null";
        }
        StringBuilder sb = new StringBuilder();
        for (Map<String, ?> param : params) {
            if (MapUtils.isEmpty(param)) {
                sb.append("null:null".concat("|"));
            } else {
                for (String p : param.keySet()) {
                    sb.append(p.concat(":").concat(String.valueOf(param.get(p))).concat("|"));
                }
            }
            sb.delete(sb.length() - 1, sb.length() - 1);
        }
        return sb.toString();
    }


    @Autowired
    public void setBaseDao(BaseMapper<T> baseDao) {
        this.baseDao = baseDao;
    }

    @Override
    public T get(ID id) {
        Cache cache = getCache();
        boolean cacheable = cacheAble();
        Object object = cacheable ? cache.get(id, ClassUtils.getSuperClassGenricType(getClass())) : null;
        T t;
        if (object == null) {
            t = baseDao.selectById(id);
            if (cacheable && t != null) {
                cache.put(id, t);
            }
        } else {
            t = (T) object;
        }
        return t;
    }

    @Override
    public T get(Map<String, Object> property) {
        String key = JsonUtil.toJsonFilter(property).toJSONString();
        Cache cache = getCache();
        boolean cacheable = cacheAble();
        Object object = cacheable ? cache.get(key, ClassUtils.getSuperClassGenricType(getClass())) : null;
        T t;
        if (object == null) {
            t = baseDao.selectOne(getExactQuery(property));
            if (cacheable && t != null) {
                cache.put(key, t);
            }
        } else {
            t = (T) object;
        }
        return t;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int remove(ID... ids) {
        int count = 0;
        for (ID id : ids) {
            baseDao.deleteById(id);
            count++;
        }
        getCache().clear();
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean edit(T t) {
        baseDao.updateById(t);

        Cache cache = getCache();
        cache.clear();
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(T t) {
        baseDao.insert(t);

        Cache cache = getCache();
        cache.clear();
        return true;
    }

    @Override
    public List<T> getList() {
        String key = "_list_";
        Cache cache = getCache();
        boolean cacheable = cacheAble();
        Object object = cacheable ? cache.get(key, ClassUtils.getSuperClassGenricType(List.class)) : null;
        List<T> list;
        if (object == null) {
            list = baseDao.selectList(null);
            if (cacheable && CollectionUtils.isNotEmpty(list)) {
                cache.put(key, list);
            }
        } else {
            list = (List<T>) object;
        }
        return list;
    }

    @Override
    public List<T> getList(Map<String, Object> exactQueryProperty) {
        return getList(exactQueryProperty, null, null);
    }

    @Override
    public List<T> getList(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty, Map<String, Boolean> sortQueryProperty) {
        return getList(exactQueryProperty, unExactQueryProperty, sortQueryProperty, null);
    }

    @Override
    public List<T> getList(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty, Map<String, Boolean> sortQueryProperty, Map<String, String> groupProperty) {
        String key = "list@" + paramMapToCacheKey(exactQueryProperty, unExactQueryProperty, sortQueryProperty, groupProperty);

        Cache cache = getCache();
        boolean cacheable = cacheAble();
        Object object = cacheable ? cache.get(key, ClassUtils.getSuperClassGenricType(List.class)) : null;
        List list;
        if (object == null) {
            Wrapper<T> wrapper = getSpecification(exactQueryProperty, unExactQueryProperty, sortQueryProperty, groupProperty);
            list = baseDao.selectList(wrapper);
            if (cacheable && CollectionUtils.isNotEmpty(list)) {
                cache.put(key, list);
            }
        } else {
            list = (List) object;
        }
        return list;
    }

    @Override
    public Pager getPage(Map<String, Object> exactQueryProperty) {
        return getPage(exactQueryProperty, null, null);
    }

    @Override
    public Pager getPage(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty, Map<String, Boolean> sortQueryProperty) {
        return getPage(exactQueryProperty, unExactQueryProperty, sortQueryProperty, null);
    }

    @Override
    public Pager getPage(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty, Map<String, Boolean> sortQueryProperty, Map<String, String> groupProperty) {
        Pager pager = PagerThreadLocal.get();
        Assert.notNull(pager, "分页器没有实例化");

        Map<String, Object> pagerMap = new HashMap<>();
        pagerMap.put("pageNo", pager.getPageNo());
        pagerMap.put("pageSize", pager.getPageSize());

        String key = "pager@" + paramMapToCacheKey(pagerMap, exactQueryProperty, unExactQueryProperty, sortQueryProperty, groupProperty);

        Cache cache = getCache();
        boolean cacheable = cacheAble();
        Object object = cacheable ? cache.get(key, ClassUtils.getSuperClassGenricType(Pager.class)) : null;
        if (object == null) {
            Wrapper<T> wrapper = getSpecification(exactQueryProperty, unExactQueryProperty, sortQueryProperty, groupProperty);
            Page<T> page = baseDao.selectPage(new Page<>(pager.getPageNo(), pager.getPageSize()), wrapper);
            pager = new Pager(pager.getPageNo(), pager.getPageSize(), page.getTotal());
            pager.setData(page.getRecords());
            if (cacheable && CollectionUtils.isNotEmpty(pager.getData())) {
                cache.put(key, pager);
            }
        } else {
            pager = (Pager) object;
        }
        return pager;
    }

    @Override
    public long getCount() {
        return getCount(null, null, null);
    }

    @Override
    public long getCount(Map<String, Object> property) {
        return getCount(property, null, null);
    }

    @Override
    public long getCount(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty) {
        return getCount(exactQueryProperty, unExactQueryProperty, null);
    }

    @Override
    public long getCount(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty, Map<String, String> groupProperty) {
        String key = "count@" + paramMapToCacheKey(exactQueryProperty, unExactQueryProperty, groupProperty);
        Cache cache = getCache();
        long count;
        boolean cacheable = cacheAble();
        Object object = cacheable ? cache.get(key, ClassUtils.getSuperClassGenricType(Long.class)) : null;
        if (object == null) {
            Wrapper<T> wrapper = getSpecification(exactQueryProperty, unExactQueryProperty, null, groupProperty);
            count = baseDao.selectCount(wrapper);
            if (cacheable && count > 0) {
                cache.put(key, count);
            }
        } else {
            count = (long) object;
        }
        return count;
    }

    /**
     * 多条件查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/9/4 14:56
     */
    private Wrapper<T> getSpecification(Map<String, Object> exactQueryProperty, Map<String, String> unExactQueryProperty, Map<String, Boolean> sortQueryProperty, Map<String, String> groupProperty) {
        QueryWrapper<T> wrapper = (QueryWrapper<T>) (MapUtils.isEmpty(sortQueryProperty) ? getDefaultSpec() : getSortQuery(sortQueryProperty));
        getExactQuery(wrapper, exactQueryProperty);
        getUnExactQuery(wrapper, unExactQueryProperty);
        getGroupQuery(wrapper, groupProperty);
        return wrapper;
    }

    /**
     * 多条件精确查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:13
     */
    protected Wrapper<T> getExactQuery(Map<String, Object> queryProperty) {
        return getExactQuery(new QueryWrapper<>(), queryProperty);
    }

    /**
     * 多条件模糊查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:09
     */
    protected Wrapper<T> getUnExactQuery(Map<String, String> queryProperty) {
        return getUnExactQuery(new QueryWrapper<>(), queryProperty);
    }

    /**
     * 多条件排序查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:14
     */
    protected Wrapper<T> getSortQuery(Map<String, Boolean> sortProperty) {
        return getSortQuery(new QueryWrapper<>(), sortProperty);
    }

    /**
     * 多条件分组查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:14
     */
    protected Wrapper<T> getGroupQuery(Map<String, String> groupProperty) {
        return getGroupQuery(new QueryWrapper<>(), groupProperty);
    }

    protected Wrapper<T> getExactQuery(QueryWrapper<T> wrapper, Map<String, Object> queryProperty) {
        if (MapUtils.isEmpty(queryProperty)) {
            return null;
        }
        for (String key : queryProperty.keySet()) {
            Object value = queryProperty.get(key);
            if (key.contains("_in")) {
                // 属性字段名称 后半部分是查询条件参数 value必须是集合类型的
                if (!(value instanceof Collection) && !value.getClass().isArray()) {
                    throw new RuntimeException("in查询条件必须为集合或数组类型");
                }
                String filed = key.substring(0, key.lastIndexOf("_"));
                if (value instanceof Collection) {
                    wrapper.in(filed, (Collection<?>) value);
                } else {
                    int length = Array.getLength(value);
                    Object[] objs = new Object[length];
                    for (int i = 0; i < length; i++) {
                        objs[i] = Array.get(value, i);
                    }
                    wrapper.in(filed, objs);
                }
                continue;
            }
            if (key.contains("_beginDate")) {
                // 前端传来的时间参数 格式需要 createTime_beginTime 前半部分是真实的
                // 属性字段名称 后半部分是查询条件参数
                String filed = key.substring(0, key.lastIndexOf("_"));
                wrapper.ge(filed, value);
                continue;
            }
            if (key.contains("_endDate")) {
                // 前端传来的时间参数 格式需要 createTime_beginTime 前半部分是真实的
                // 属性字段名称 后半部分是查询条件参数
                String filed = key.substring(0, key.lastIndexOf("_"));
                wrapper.le(filed, value);
                continue;
            }
            if (key.contains("_gt")) {
                // 前端传来的参数 格式 id_ge 前半部分是真实的
                // 属性字段名称 后半部分是查询条件参数  大于
                String filed = key.substring(0, key.lastIndexOf("_"));
                wrapper.gt(filed, value);
                continue;
            }
            if (key.contains("_ge")) {
                // 前端传来的参数 格式 id_ge 前半部分是真实的
                // 属性字段名称 后半部分是查询条件参数  大于等于
                String filed = key.substring(0, key.lastIndexOf("_"));
                wrapper.ge(filed, value);
                continue;
            }
            if (key.contains("_le")) {
                // 前端传来的参数 格式 id_ge 前半部分是真实的
                // 属性字段名称 后半部分是查询条件参数  小于等于
                String filed = key.substring(0, key.lastIndexOf("_"));
                wrapper.lt(filed, value);
                continue;
            }
            if (key.contains("_le")) {
                // 前端传来的参数 格式 id_ge 前半部分是真实的
                // 属性字段名称 后半部分是查询条件参数  小于等于
                String filed = key.substring(0, key.lastIndexOf("_"));
                wrapper.le(filed, value);
                continue;
            }
            wrapper.eq(key, queryProperty.get(key));
        }
        return wrapper;
    }

    /**
     * 多条件模糊查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:09
     */
    protected Wrapper<T> getUnExactQuery(QueryWrapper<T> wrapper, Map<String, String> queryProperty) {
        if (MapUtils.isEmpty(queryProperty)) {
            return null;
        }

        for (String key : queryProperty.keySet()) {
            if (queryProperty.get(key) == null || queryProperty.get(key).length() == 0) {
                continue;
            }
            wrapper.like(key, queryProperty.get(key));
        }
        return wrapper;
    }

    /**
     * 多条件排序查询
     * true ASC, false DESC
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:14
     */
    protected Wrapper<T> getSortQuery(QueryWrapper<T> wrapper, Map<String, Boolean> sortProperty) {
        if (MapUtils.isEmpty(sortProperty)) {
            return null;
        }

        for (String key : sortProperty.keySet()) {
            if (sortProperty.get(key) == null) {
                continue;
            }
            wrapper.orderBy(true, sortProperty.get(key), key);
        }
        // mybatis-plus 排序需要有主键，不然会造成数据的重复
        // 默认判断是否存在id，如果不存在则按照id降序
        if (!sortProperty.containsKey("id")) {
            wrapper.orderBy(true, false, "id");
        }
        return wrapper;
    }

    /**
     * 多条件分组查询
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 20:14
     */
    protected Wrapper<T> getGroupQuery(QueryWrapper<T> wrapper, Map<String, String> groupProperty) {
        if (MapUtils.isEmpty(groupProperty)) {
            return null;
        }
        for (String key : groupProperty.keySet()) {
            if (groupProperty.get(key) == null) {
                continue;
            }
            wrapper.groupBy(key);
        }
        return wrapper;
    }

    /**
     * 默认Id降序
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/6/28 21:04
     */
    public Wrapper<T> getDefaultSpec() {
        Wrapper<T> wrapper = getSortQuery(new HashMap<String, Boolean>(1) {
            private static final long serialVersionUID = -3371120713938289395L;

            {
                put("id", false);
            }
        });
        return wrapper;
    }
}